Skip to Content

38장 브라우저의 렌더링 과정

렌더링 : HTML, CSS, JS 로 작성된 문서를 파싱하여 브라우저에 시각적으로 출력하는 것

Node.js(자바스크립트 런타임 환경)의 등장으로 자바스크립트는 웹 브라우저를 벗어나 서버 사이드 애플리케이션 개발에서도 사용할 수 있는 범용 개발 언어가 되었다. 그럼에도 자바스크립트가 가장 많이 사용되는 분야는 웹 브라우저 환경에서 동작하는 웹/앱 클라이언트 사이드이다.

대부분의 프로그래밍 언어는 OS나 가상머신 위에서 실행되지만 웹 애플리케이션의 클라이언트 사이드 자바스크립트는 브라우저에서 HTML, CSS와 함께 실행된다.

따라서 브라우저 환경을 고려해야 더 효율적인 JS 프로그래밍이 가능하다.

image.png

[브라우저의 렌더링 과정]

  1. 브라우저는 HTML, CSS, 자바스크립트, 이미지, 폰트 파일 등 렌덩링에 필요한 리소스를 요청하고 서버로부터 응답 받는다.
  2. 브라우저의 렌더링 엔진은 서버로부터 응답된 HTML 과 CSS 를 파싱하여 DOM과 CSSOM 을 생성하고 이들을 결합하여 렌더 트리를 생성한다.
  3. 브라우저의 자바스크립트 엔진은 서버로부터 응답된 자바스크립트를 파싱하여 AST를 생성하고 바이트코드로 변환하여 실행한다. 이때 자바스크립트는 DOM API를 통해 DOM 이나 CSSOM 을 변경할 수 있다. 변경된 DOM과 CSSOM은 다시 렌더트리로 결합된다.
  4. 렌더 트리를 기반으로 HTML 요소의 레이아웃을 계산하고 브라우저 화면에 HTML 요소를 페인팅한다.

DOM(Document Object Model문서 객체 모델) : 웹 브라우저가 HTML 문서를 이해하고 조작할 수 있도록 각 요소를 트리 구조의 객체로 변환한 인터페이스

CSSOM(CSS Object Model) : 브라우저가 웹 페이지의 CSS(스타일) 정보를 객체 형태로 구조화하여 표현하는 모델

38.1 요청과 응답

브라우저의 핵심 기능 : 필요한 리소스를 서버에 요청하고 응답을 받아 브라우저에 렌더링 하는 것

리소스 종류

  • HTML, CSS, 자바스크립트
  • 이미지, 폰트

위와 같이 필요한 리소스는 모두 서버에 존재하므로 이를 요청하고, 응답을 파싱하여 렌더링하는 것.

과정

1️. 브라우저 주소창에 URL을 입력한다.

https://poiemawweb.com

이 순간 브라우저는 “이 주소가 가리키는 서버에게 뭔가를 요청해야겠다” 라고 인지한다.

2️. DNS: 이 도메인 이름이 어디 서버인지 찾기

문제는 서버가 문자 주소(poie…com) 를 이해하지 못한다는 점이다.

서버는 IP 주소(숫자) 만 이해할 수 있다.

→ 그래서 브라우저는 먼저 DNS(Domain Name System) 에게 “poiemawweb.com”이라는 이름은 어떤 IP 주소 를 가지고 있는지 물어본다.

이에 대해 DNS는 poiemawweb.com → 185.199.109.153 이런식으로 IP 주소를 알려준다. → 이제 브라우저는 어디로 요청을 보내야 하는지 알게 된다.

3️. 서버에 요청(Request)을 보낸다.

브라우저의 요청 ex

GET / HTTP/1.1 Host: poiemawweb.com
  • /루트 요청
  • “특정 파일을 달라고 안 했으니, 기본 페이지를 달라”는 뜻

대부분의 서버는 이 경우 index.html 을 기본으로 응답하도록 설정돼 있다. 즉, https://poiemawweb.com  는 사실https://poiemawweb.com/index.html  을 요청한 것과 같다.

4️. 서버가 응답(Response)을 보낸다.

응답 ex

  • HTML 파일 (index.html)
  • 상태 코드 200 OK
  • 기타 헤더 정보들

이것들은 개발자 도구 Network 패널에서 확인가 ⇒ 여기까지가 첫 번째 요청

5️. CSS, JS, 이미지 요청 처리

Network 패널을 보면

index.html 하나만 요청했는데 왜 CSS, JS, 이미지 요청이 줄줄이 나오지?” 의문이 든다. 하지만 이것이 브라우저 렌더링 엔진의 핵심 동작 이다.


6️. HTML을 읽다가 외부 파일 발견

브라우저는 index.html위에서 아래로 읽게 됨(파싱).

그런데 읽다가 아래와 같은 태그를 만나면

<linkrel="stylesheet"href="style.css"> <scriptsrc="app.js"></script> <imgsrc="banner.png">

브라우저는 CSS, JS, 이미지 파일을 불러와야 한다는 것을 알고 추가요청을 자동으로 서버에게 보내게 됨.

이 요청들은 우리가 직접 주소창에 친 게 아니라, HTML 안에 적혀 있어서 브라우저가 알아서 보낸 요청.


+Network 패널에 요청이 많은 이유

  • 주소창 요청 → HTML
  • HTML 파싱 중 → CSS 요청
  • HTML 파싱 중 → JS 요청
  • HTML 파싱 중 → 이미지, 폰트 요청

그래서 Network 패널에 수십 개 요청이 보이는 것.

요약

  • 브라우저는 URL을 입력받으면 DNS를 통해 IP 주소를 얻고 서버에 요청을 보낸다.
  • 서버로부터 HTML을 응답받은 뒤, 렌더링 엔진이 HTML을 파싱하면서 CSS, JavaScript, 이미지 등의 외부 리소스를 추가로 요청하여 화면을 완성한다.

38.2 HTTP 1.1과 HTTP 2.0

HTTP : 브라우저와 서버가 “어떻게 말할지” 정해놓은 약속(규칙)

HTTP/1.1의 동작 방식

image.png

요청 → 응답 요청 → 응답 요청 → 응답

HTML 안에 이런 리소스가 있다고 가정

<link rel="stylesheet" href="style.css"> <script src="app.js"></script>
  • index.html 요청 → 응답
  • style.css 요청 → 응답
  • app.js 요청 → 응답
  • 끝나면 커넥션 종료

핵심 동작 원리

  • 요청 하나 보내고 → 응답 받을 때까지 다음 요청 못 함
  • 반드시 순서대로 처리

이로인해 CSS, JS, 이미지가 많아질수록 대기시간이 계속 누적된다.(= 병목현상)

HTTP/2 의 동작 방식

image.png

요청 ┐ 요청 ├── 동시에 처리 요청 ┘

동작원리 : “같은 연결에서 여러 요청을 동시에 보내자”

  1. index.html 요청
  2. 같은 커넥션에서
    • style.css 요청
    • app.js 요청
    • 이미지 요청
  3. 서버가 동시에 응답
  • 요청/응답이 섞여서(multiplexing) 오갈 수 있음
  • 기다릴 필요 없음

그래서 HTTP/2가 왜 빠를까?

1️. 대기 시간이 없다

  • HTTP/1.1: 앞 요청 끝날 때까지 대기
  • HTTP/2: 동시에 처리

2️.연결을 계속 유지

  • 매번 연결 열고 닫는 비용 감소

3️. 리소스 많을수록 차이 커짐

  • 요즘 웹: JS, CSS, 이미지 수십 개
  • HTTP/2에 훨씬 유리
구분HTTP/1.1HTTP/2
요청 처리 방식하나씩 순서대로여러 개를 동시에
연결(Connection)요청-응답 반복연결 유지 + 다중 처리
속도느림 (리소스 많을수록)빠름
문제점병목 발생병목 해결

38.3 HTML 파싱과 DOM 생성

image.png


HTML 파싱과 DOM 생성이란?

  • HTML 파싱(parsing): 브라우저가 HTML 텍스트를 읽고 해석하는 과정
  • DOM 생성: 해석한 결과를 바탕으로, 브라우저가 HTML을 트리 구조(객체 구조) 로 만들어 메모리에 올리는 것
    • 이 트리를 DOM(Document Object Model) 이라고 함.

한 줄로:

브라우저는 HTML을 위에서 아래로 읽으면서 “노드(node)”를 만들고, 그 노드들을 연결해 DOM 트리를 만든다.


전체 큰 흐름 7단계

1) 서버로부터 HTML 응답을 받음

브라우저가 index.html을 받아옴.

2) HTML을 “바이트 → 문자열”로 디코딩

응답 본문은 바이트(데이터)고, 브라우저는 이를 문자로 바꿈.

(인코딩이 utf-8 같은 걸로 지정됨)

3) 토크나이징(Tokenizing)

HTML 문자열을 “의미 있는 조각”으로 자름.

ex)

<div class="box">Hi</div>

이런 식으로 토큰이 만들어짐:

  • <div class="box"> → 시작 태그 토큰
  • Hi → 텍스트 토큰
  • </div> → 종료 태그 토큰

4) 파싱(Parsing) + 노드 생성(Node creation)

토큰을 규칙에 맞게 해석해서 노드를 만든다.

  • 요소 노드(Element node): div, p, img 같은 태그
  • 텍스트 노드(Text node): 태그 사이의 글자
  • 주석 노드(Comment node): <!-- -->

5) DOM 트리 구성(Tree building)

만든 노드들을 부모-자식 관계로 연결해 트리를 만든다.

ex)

<html> <body> <p>Hello</p> </body> </html>

DOM 트리 느낌:

Document ㄴ html ㄴ body ㄴ p ㄴ "Hello"(텍스트 노드)

6) 파싱 중 외부 리소스 만나면 추가 요청

HTML을 읽다가 이런 걸 만나면:

<link rel="stylesheet" href="style.css" /> <script src="app.js"></script> <img src="a.png" />

브라우저는 해당 파일을 가져오기 위해 추가 요청을 보냄.

  • CSS, JS, 이미지, 폰트 등

7) DOM 생성 완료

HTML 문서 끝까지 파싱하면 DOM 트리가 완성됨!


38.4 CSS 파싱과 CSSOM 생성

CSS 파싱: 브라우저가 CSS 파일을 읽고 해석하는 과정

CSSOM 생성: 해석한 CSS를 기반으로 스타일 규칙 트리를 만드는 것. 이 트리를 CSSOM(CSS Object Model) 이라고 부른다.

index.html

<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8" /> <link rel="stylesheet" href="style.css" /> ... </head> </html>

위 코드엔 CSS 파일을 로드하는 link 태그가 있다. 이후 진행 과정은 아래와 같다.

  1. 렌더링 엔진은 meta태그까지 순차적으로 해석한다
  2. 이후 ink 태그를 만나면 DOM 생성을 일시 중단한다.
  3. link 태그의 href 어트리뷰트에 지정된 CSS 파일을 서버에 요청한다.
body { font-size: 18px; } ul { list-style-type: none; }

위와 같은 CSS파일이 응답되었다고 할 때,서버로부터 CSS 파일이 응답되면

렌더링 엔진은 HTML 과 동일하게 (바이트 →문자→토큰→노드→CSSOM)을 거쳐 CSS를 파싱하여 CSSOM을 생성한다.

  • 이때 CSSOM은 CSS의 상속을 반영하여 생성된다.아래 그림 참고

image.png

38.5 렌더 트리 생성(DOM + CSSOM)

  • 렌더링 엔진은 서버로부터 응답된 HTML과 CSS를 파싱하여 각각 DOM과 CSSOM을 생성한다. ← 이 둘은 렌더링을 위해 렌더트리로 결합된다!
    • 렌더링을 위한 것인만큼 브라우저에 렌더링되지않는 노드(meta, script 태그 등)는 포함되지 않는다. image.png DOM + CSSPOM ⇒ Render Tree 의 모습을 나타낸 도식화 그림

image.png

  • painting : 화면에 픽셀을 렌더링하는 과정
  • 위 렌더링 과정이 반복해서 발생(리렌더링)되는 것은 비용이 많이 들고 성능에 악영향을 미치므로 주의하자.

38.6 자바스크립트 파싱과 실행

image.png

파싱을 “멈추게 하는 것” vs “안 멈추는 것”

  1. <img>는 보통 파싱을 안 멈춤
<img src="a.png" />
  • 이미지 요청은 보내지만,
  • HTML 파싱은 계속 진행하는 경우가 많다. (이미지는 없어도 DOM 자체는 만들 수 있으니까)

2) <link rel="stylesheet">는 “렌더링”을 막을 수 있음 (Render-blocking)

<link rel="stylesheet" href="style.css" />
  • HTML 파싱 자체는 계속되기도 하지만,
  • CSS가 다 적용되기 전에는 화면이 “이상하게” 보일 수 있어서
  • 브라우저는 CSSOM이 준비될 때까지 렌더링을 늦추는 경향이 있음.

즉,

CSS는 DOM 파싱을 완전히 멈추진 않더라도, “화면 그리기(render)”는 늦출 수 있다.


3) <script>는 기본적으로 HTML 파싱을 멈춤 (Parsing-blocking)

<script src="app.js"></script>

왜 멈추냐면

  • JS는 DOM을 바꿀 수 있음.
    • 예: document.querySelector('p').remove()
  • 그래서 브라우저 입장에서는 DOM을 만들고 있는 도중에 JS가 DOM을 바꾸면 위험하기 때문에. 스크립트를 만나면 파싱을 잠깐 멈추고
  1. JS 파일 다운로드

  2. JS 실행

  3. 다시 HTML 파싱 재개

    이런 순서로 진행한다.


예제로 보는 “DOM 생성 흐름”

예시 HTML

<!doctype html> <html> <head> <link rel="stylesheet" href="style.css" /> <script src="app.js"></script> </head> <body> <h1>Hello</h1> </body> </html>

브라우저 내부에서 일어나는 일 (흐름)

  1. <html> 토큰 → html 노드 생성
  2. <head> → head 노드 생성
  3. <link ...> 발견 → style.css 요청 시작
  4. <script ...> 발견 → HTML 파싱 멈춤
  5. app.js 다운로드 → 실행
  6. 파싱 재개 → <body><h1> 노드 생성
  7. DOM 완성

38.7 리플로우와 리페인트

image.png

  • 리플로우 : 레이아웃을 다시 계산하는 것.
    • 노드 추가/삭제
    • 요소의 크기/위치 변경
    • 윈도우 리사이징 위 와 같은 영향으로 발생한다.
  • 리페인트 : 재결합된 렌더 트리를 기반으로 다시 페인트 하는 것

이 둘은 순차적으로 동시 실행되는 것은 아니고, 레이아웃에 영향이 없는 변경은 리플로우 없이 리페인트만 실행된다.

38.8 자바스크립트 파싱에 의한 HTML 파싱 중단

브라우저는 <script> 태그를 만나면 기본적으로 HTML 파싱을 멈춘다.

왜 HTML 파싱을 멈출까?

JavaScript는 DOM을 변경할 수 있기 때문이다.

ex) 아래 코드가 js에 있다고 하자.

document.querySelector('h1').remove();

만약 브라우저가 HTML을 계속 파싱하면서 DOM을 만들고 있는데, 중간에 JS가 DOM을 바꿔버리면 DOM 구조가 꼬여버릴 수 있음

실제 동작 과정

<!doctype html> <html> <head> <script src="app.js"></script> </head> <body> <h1>Hello</h1> </body> </html>

브라우저 내부에서 일어나는 일

  1. HTML 파싱 시작
  2. <script src="app.js"> 발견
  3. HTML 파싱 중단
  4. app.js 파일 다운로드
  5. JS 파싱 + 실행
  6. 실행이 끝나면
  7. HTML 파싱 재개

위 과정이 자바스크립트로 인한 HTML 파싱 중단.

심지어 아래 코드처럼 CSS와 함께 있으면 더 늦어질 수 있음.

getComputedStyle(element)

그래서 CSS 파일이 아직 안 왔으면 JS 의 실행도 기다림. 일단 CSS 부터 도착을 해야하기 때문!

CSS → JS → HTML 파싱 재개과정으로 진행되며 렌더링 지연의 원인이 된다.

따라서 <script> 위치가 중요하다.

<head>에 두지 않고, body 맨 아래 두는 것이 안정적이다.

<body> <h1>Hello</h1> <script src="app.js"></script> </body>

38.9 script 태그의 async/defer 어트리뷰트

38.8 의 문제를 해결하는데 더 좋은 해결책은 deferasync 이다.

DOM 생성이 중단되는 문제를 근복적으로 해결하기 위해 추가된 것이니 아래서 살펴보자.

우선, async, defer 모두 src 어트리뷰트를 통해 외부 자바스크립트 파일을 로드하는 경우에만 사용할 수 있다. 즉, src 어트리뷰트가 없는 인라인 자바스크립트에는 사용할 수 없다.

  1. async

image.png

<script src="app.js" async></script>

동작 방식:

  • HTML 파싱 & 외부 JS 파일의 로드가 비동기적으로 동시 진행
  • 단, JS의 파싱과 실행은 JS 파일의 로드가 끝나는 즉시 실행
    • 이때 HTML 파싱이 중단된다.
  • 여러개의 script 태그에 async 어트리뷰트를 지정하면 실행 순서는 보장 안 됨
    • 따라서 순서 보장이 필요한 script 태그에는 async 어트리뷰트를 지정하지 않아야 함.
  1. defer

image.png

<script src="app.js" defer></script>

동작 방식:

  • HTML 파싱 & 외부 JS 파일의 로드가 비동기적으로 동시 진행
  • DOM 생성 완료 후 JS가 파싱,실행된다.
    • DOM 생성 완료 이후 실행되어야 할 JS에 유용하다.

따라서 DOM을 조작하는 JS에 최적이다.

퀴즈

퀴즈를 풀어봅시다!


Q1. 브라우저 렌더링 과정의 순서

다음 중 브라우저가 화면을 그리기까지의 올바른 순서는?

① HTML 파싱 → CSSOM 생성 → DOM 생성 → Render Tree 생성 → Paint
② HTML 파싱 → DOM 생성 → CSS 파싱 → CSSOM 생성 → Render Tree 생성
③ HTML 파싱 → DOM 생성 → CSSOM 생성 → Render Tree 생성 → Layout → Paint
④ HTML 파싱 → Render Tree 생성 → DOM 생성 → CSSOM 생성 → Paint

정답 보기

정답: ③

브라우저는 HTML을 파싱해 DOM을 생성하고, CSS를 파싱해 CSSOM을 생성한 뒤 DOM과 CSSOM을 결합해 Render Tree를 만든다. 이후 Layout(레이아웃 계산)과 Paint(그리기)를 거쳐 화면을 렌더링한다.


Q2. JavaScript로 인한 HTML 파싱 중단

다음 중 <script> 태그가 HTML 파싱을 중단시키는 가장 근본적인 이유는?

① JavaScript 파일의 용량이 크기 때문이다.
② JavaScript는 CSSOM 생성을 항상 기다려야 하기 때문이다.
③ JavaScript는 DOM 구조를 변경할 수 있기 때문이다.
④ 브라우저는 script 태그를 항상 마지막에 실행하기 때문이다.

정답 보기

정답: ③

JavaScript는 DOM을 수정할 수 있으므로, 브라우저는 HTML 파싱 도중 script 태그를 만나면 DOM 일관성을 보장하기 위해 파싱을 일시 중단하고 JavaScript를 실행한다.


Q3. CSS와 JavaScript가 렌더링에 미치는 영향

다음 설명 중 옳은 것은?

① CSS 파일은 HTML 파싱과 렌더링 모두를 항상 중단시킨다.
② JavaScript는 defer 속성을 사용해도 HTML 파싱을 중단시킨다.
③ CSSOM이 완성되지 않아도 Render Tree는 생성될 수 있다.
④ 기본 <script> 태그는 HTML 파싱을 중단시키지만, defer는 그렇지 않다.

정답 보기

정답: ④

기본 <script> 태그는 HTML 파싱을 중단시키지만,
defer 속성을 사용하면 HTML 파싱을 중단하지 않고
DOM 생성이 완료된 후 JavaScript가 실행된다.

관련 아티클

Naver D2 – 브라우저는 어떻게 동작하는가

https://d2.naver.com/helloworld/2922312 

Last updated on